package com.yy.util.bitmap;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Environment;
import android.text.TextUtils;
import android.util.LruCache;
import android.util.SparseArray;
import android.widget.ImageView;

import com.yy.util.R;
import com.yy.util.http.AppHttpUtil;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;

/**
 * Created by Administrator on 2015/5/5.
 */
public class AsyncBitmapHelper extends BitmapHelper {

    private final static String TAG = AsyncBitmapHelper.class.getSimpleName();

    private String mUrl;
    private Context mContext;
    private BitmapDiskLruCache mDiskLruCache;
    private boolean mDiskCacheStarting = true;
    private boolean mIsLoadDiskLruCacheOver;

    private Object diskCacheLock = new Object();

    private final static int DISK_CACHE_SIZE = 1024 * 1024 * 10;
    private final static LruCache<String, Bitmap> MEMORY_CACHE;

    static {
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        final int cacheSize = maxMemory / 8;
        MEMORY_CACHE = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getByteCount() / 1024;
            }
        };
    }

    public AsyncBitmapHelper(Context context) {
        mContext = context;
    }

    /**
     * 初始化本地缓存磁盘
     * @param uniqueName
     */
    public void initDiskLruCache(String uniqueName) {
        File cacheDir = getDiskCacheDir(uniqueName);
        InitDiskCacheTask initDiskCacheTask = new InitDiskCacheTask();
        initDiskCacheTask.execute(cacheDir);
    }

    /**
     * 加载 Bitmap 数据到 ImageView 上
     * @param mImageView
     */
    public void loadBitmap(final String url, ImageView mImageView) {
        final Bitmap bitmapFromMemory = getBitmapFromMemory(url);
        if(bitmapFromMemory == null) {
            final Bitmap bitmapFromDisk = getBitmapFromDiskLruCache(url);
            if(bitmapFromDisk == null) {
                if(cancelPotentialWork(url, mImageView)) {
                    final BitmapWorkerTask bitmapWorkerTask = new BitmapWorkerTask(mImageView);
                    final Bitmap mPlaceHolderBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher);
                    final AsyncDrawable asyncDrawable = new AsyncDrawable(mContext.getResources(), mPlaceHolderBitmap, bitmapWorkerTask);
                    mImageView.setImageDrawable(asyncDrawable);
                    bitmapWorkerTask.execute(url);
                }
            } else {
                mImageView.setImageBitmap(bitmapFromDisk);
            }
        } else {
            mImageView.setImageBitmap(bitmapFromMemory);
        }
    }

    @Override
    public Bitmap decode(BitmapFactory.Options options) {
        AppHttpUtil http = new AppHttpUtil();
        SparseArray<InputStream> inputStream = null;
        try {
            inputStream = http.httpGet(mUrl);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return BitmapFactory.decodeStream(inputStream.valueAt(0), null, options);
    }

    private boolean cancelPotentialWork(String url, ImageView mImageView) {
        final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(mImageView);
        if(bitmapWorkerTask != null) {
            final String bitmapUrl = bitmapWorkerTask.imageUrl;
            if(TextUtils.isEmpty(bitmapUrl) && bitmapUrl != url) {
                bitmapWorkerTask.cancel(true);
            } else {
                return false;
            }
        }
        return true;
    }

    private BitmapWorkerTask getBitmapWorkerTask(ImageView mImageView) {
        if(mImageView != null) {
            final Drawable drawable = mImageView.getDrawable();
            if(drawable instanceof AsyncDrawable) {
                final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
                return asyncDrawable.getBitmapWorkerTask();
            }
        }
        return null;
    }

    /**
     * 获得本地缓存路径
     * @param uniqueName
     * @return
     */
    private File getDiskCacheDir(String uniqueName) {
        final String cachePath = Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||
                !Environment.isExternalStorageRemovable() ? mContext.getExternalCacheDir().getPath() :
                mContext.getCacheDir().getPath();
        return new File(cachePath + File.separator + uniqueName);
    }

    /**
     * 增加缓存到内存中
     * @param key
     * @param value
     */
    private void addBitmap2MemoryCache(String key, Bitmap value) {
        if(getBitmapFromMemory(key) == null) {
            MEMORY_CACHE.put(key, value);
        }
    }

    /**
     * 从内存中取得缓存
     * @param key
     * @return
     */
    private Bitmap getBitmapFromMemory(String key) {
        return MEMORY_CACHE.get(key);
    }

    /**
     * 增加缓存到本地磁盘
     * @param key
     * @param bitmap
     */
    private void addBitmap2DiskLruCache(String key, Bitmap bitmap) {
        synchronized (diskCacheLock) {
            if(mDiskLruCache != null && getBitmapFromDiskLruCache(key) == null) {
                mDiskLruCache.put(key, bitmap);
            }
        }
    }

    /**
     * 从本地磁盘取得缓存文件，必须初始化完毕后
     * @param key
     * @return
     */
    private Bitmap getBitmapFromDiskLruCache(String key) {
        synchronized (diskCacheLock) {
            while (mDiskCacheStarting) {
                try {
                    diskCacheLock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            if(mDiskLruCache != null) {
                final Bitmap bitmapFromDiskLruCache = mDiskLruCache.get(key);
                if(bitmapFromDiskLruCache != null) {
                    addBitmap2MemoryCache(key, bitmapFromDiskLruCache);
                }
                return bitmapFromDiskLruCache;
            }
        }
        return null;
    }

    /**
     * 为了更有效的处理内存， 那些视图的子组件会在用户滑动屏幕时被循环使用。
     * 如果每一个子视图都触发一个AsyncTask，那么就无法确保当前视图在结束task时，
     * 分配的视图已经进入循环队列中给另外一个子视图进行重用。而且,无法确保所有的异步任务能够按顺序执行完毕。
     * 创建一个专用的 Drawable 子类来保存一个可以回到当前工作任务的引用。
     * 在这种情况下， BitmapDrawable 被用来作为占位图片， 它可以在任务结束时显示到ImageView中。
     */
    private static class AsyncDrawable extends BitmapDrawable {

        private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskWeakReference;

        public AsyncDrawable(Resources resources, Bitmap bitmap, BitmapWorkerTask task) {
            super(resources, bitmap);
            this.bitmapWorkerTaskWeakReference = new WeakReference<BitmapWorkerTask>(task);
        }

        public BitmapWorkerTask getBitmapWorkerTask() {
            return bitmapWorkerTaskWeakReference.get();
        }
    }

    /**
     * 异步加载数据任务
     */
    private class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {

        private final WeakReference<ImageView> weakReference;
        private String imageUrl;

        public BitmapWorkerTask(ImageView mImageView) {
            weakReference = new WeakReference<ImageView>(mImageView);
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            imageUrl = params[0];
            setUrl(imageUrl);
            final Bitmap bitmap = decodeSampleBitmap(100, 100);
            addBitmap2DiskLruCache(imageUrl, bitmap);
            addBitmap2MemoryCache(imageUrl, bitmap);
            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            if(isCancelled()) {
                bitmap = null;
            }
            if(weakReference != null && bitmap != null) {
                final ImageView mImageView = weakReference.get();
                final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(mImageView);
                if(this == bitmapWorkerTask && mImageView != null) {
                    mImageView.setImageBitmap(bitmap);
                }
            }
        }
    }

    /**
     * 子线程中初始化本地磁盘
     */
    private class InitDiskCacheTask extends AsyncTask<File, Void, Void> {

        @Override
        protected Void doInBackground(File... params) {
            synchronized (diskCacheLock) {
                File cacheDir = params[0];
                mDiskLruCache = BitmapDiskLruCache.open(mContext, cacheDir, 1, DISK_CACHE_SIZE);
                mDiskCacheStarting = false;
                diskCacheLock.notifyAll();
            }
            return null;
        }
    }

    public void setUrl(String url) {
        mUrl = url;
    }
}
